import logging
import sys

import networkx as nx
import pandas as pd
import collections
# matplotlib.use('TkAgg')

from group_profile.neo4j_util import Neo4jDriver


class GroupProfile:

    def get_group(self, group_id):
        if group_id not in self.groups:
            return None
        logging.info('正在获取群体画像')
        group = self.groups[group_id]

        #获取异网手机号
        records, _, _ = self.neo4j_driver.execute(
            f"match (e:User)-[has:has_phone]->(p1:Phone)-[c:called]->(p2:Phone) where e.community_id = {group_id} return p2.MSISDN_SENSID")
        potentials = list(record.data()['p2.MSISDN_SENSID'] for record in records)
        groupinfo = (f'{group_id} 号群体画像：\n'
                     f'    用户数：    {group.user_size}\n'
                     f'    基站数：    {group.lac_size}\n'
                     f'    群体主要地址：    {group.lac_max_add}\n'
                     f'    群体主要经纬度：    ({group.lac_max_lat},{group.lac_max_lgtd})\n'
                     f'    群体城市：    {group.lac_max_city}\n'
                     f'    群体行政区：    {group.lac_max_area}\n'
                     f'    用户平均月资费：    {group.user_avg_fee}\n'
                     f'    用户高频APP(最高流量)：    {group.max_app_usage_name}-{group.max_app_usage_value}MB\n'
                     f'    用户高频APP(最高次数)：    {group.max_app_cnt_name}-{group.max_app_cnt_value}次\n'
                     f'    用户高频APP(最长时间)：    {group.max_app_time_name}-{group.max_app_time_value}MIN\n'
                     f'    群组标签：    {group.tags}\n'
                     f'    异网手机号码：    {potentials}\n'
                     f'    成群原因：    地理位置相近，长时间使用同基站\n'
                     f'\n')
        logging.info(groupinfo)
        return groupinfo

    def draw_group(self, group_id):
        if group_id not in self.groups:
            return None

        subgraph_cypher = (
            f'match (e:Lac)-[r]-(e2:User) where e.community_id = {group_id} and e2.community_id = {group_id} '
            f'optional match (e2)-[has:has_phone]->(p1:Phone)-[c:called]->(p2:Phone) return *')
        logging.info(f'群体子图查询cypher: {subgraph_cypher}')
        records, summary, keys = self.neo4j_driver.execute(
            subgraph_cypher)
        subgraph = list(record.data() for record in records)
        logging.info(f'群体子图详细数据：    {subgraph}\n'
                     )
        # group = self.groups[group_id]

        # nx.draw(group.subgraph)
        # plt.show()

    def get_statics(self):
        logging.info('正在获取群体统计数据')
        tagsmap = {}
        sizemap = {}
        tagstotal = 0
        for id in self.groups:
            group = self.groups[id]
            for tag in group.tags:
                tagstotal += 1
                if tag in tagsmap:
                    tagsmap[tag] += 1
                else:
                    tagsmap[tag] = 1
            if int(group.user_size / 10) * 10 in sizemap:
                sizemap[int(group.user_size / 10) * 10] += 1
            else:
                sizemap[int(group.user_size / 10) * 10] = 1
        statinfo = (f'群体统计：\n'
                    f'    群组总数：    {len(self.groups)}\n'
                    f'    标签总数：    {tagstotal}\n'
                    f'    标签总类：    {len(tagsmap)}\n'
                    ' \n人数占比情况：'
                    f'\n')
        sizes = list(sizemap.keys())
        sizes.sort()
        for size in sizes:
            statinfo += f'    {size}-{size + 10}人群体：    {sizemap[size] * 100 / len(self.groups)}%\n'
        statinfo += '\n标签情况：\n'
        for tag in tagsmap:
            statinfo += f'    具有《{tag}》标签群体：    {tagsmap[tag] * 100 / len(self.groups)}%\n'
        logging.info(statinfo)
        return statinfo

    def get_group_by_city(self, city):
        logging.info('正在获取地区群体数据')
        result = []
        for id in self.groups:
            group = self.groups[id]
            if (group.lac_max_add == city or
                    group.lac_max_add in city or
                    city in group.lac_max_add):
                result.append(group)
        return result

    def __init__(self):
        logging.info("处理自然人数据")

        self.groups = {}
        base_path = sys.path[0]

        data_path = f'{base_path[:base_path.index('insight_agent')]}/insight_agent/agent_rule_new_customer_lijia/data'
        # 数据读取
        personal_market = pd.read_csv(f'{data_path}/personal_market_data.csv')
        bill_data = pd.read_csv(f'{data_path}/bill_data.csv')
        lac_data = pd.read_csv(f'{data_path}/lac_data.csv')
        user_app_data = pd.read_csv(f'{data_path}/user_app_data.csv')

        bill_data.set_index('USER_ID', inplace=True)
        user_app_data.set_index('USER_ID', inplace=True)
        lac_data.set_index('LAC_CODE', inplace=True)

        # logging.info(personal_market.head())
        # logging.info(bill_data.head())
        # logging.info(lac_data.head())
        # logging.info(user_app_data.head())

        # louvain社群挖掘
        graph = nx.Graph()
        for _, row in personal_market.iterrows():
            graph.add_edge(row['USER_ID'], row['LAC_CODE'])

        partition = nx.community.louvain_communities(graph)
        GroupProfile = collections.namedtuple('GroupProfile',
                                              'id '
                                              'user_size '
                                              'lac_size '
                                              'lac_max_add '
                                              'lac_max_lat '
                                              'lac_max_lgtd '
                                              'lac_max_city '
                                              'lac_max_area '
                                              'user_avg_fee '
                                              'max_app_usage_name '
                                              'max_app_usage_value '
                                              'max_app_cnt_name '
                                              'max_app_cnt_value '
                                              'max_app_time_name '
                                              'max_app_time_value '
                                              'tags '
                                              'subgraph'
                                              )

        self.neo4j_driver = Neo4jDriver()

        for i in range(len(partition)):
            users = list(filter(lambda x: x.__contains__('user'), partition[i]))
            lacs = list(filter(lambda x: not x.__contains__('user'), partition[i]))
            subgraph = nx.Graph()
            for _, row in personal_market.iterrows():
                if row['USER_ID'] in users and row['LAC_CODE'] in lacs:
                    subgraph.add_edge(row['USER_ID'], row['LAC_CODE'])

            user_size = len(users)  # 用户数
            lac_size = len(lacs)  # 基站数

            lac_add = lac_data.loc[lacs]
            lac_max_degree = 0
            lac_max = None
            for user in users:
                self.neo4j_driver.execute(
                    f'match (user:User) where user.USER_ID = "{user}" set user.community_id = {i}')
            for lac in lacs:
                self.neo4j_driver.execute(f'match (lac:Lac) where lac.LAC_CODE = "{lac}" set lac.community_id = {i}')
                degree = subgraph.degree[lac]
                if degree > lac_max_degree:
                    lac_max = lac
                    lac_max_degree = degree

            lac_max_add = lac_data.loc[lac_max]['BST_ADDR']
            lac_max_lat = lac_data.loc[lac_max]['BST_LAT']
            lac_max_lgtd = lac_data.loc[lac_max]['BST_LGTD']
            lac_max_city = lac_data.loc[lac_max]['BST_ADDR'][:3]
            lac_max_area = lac_data.loc[lac_max]['BST_ADDR'][3:6]

            user_avg_fee = sum(bill_data.loc[users]['ARECV_TOT']) / user_size  # 用户平均月资费
            user_app_usage = user_app_data.loc[users]
            max_app_usage = \
                user_app_usage.groupby('APP_NAME').agg({'USE_DATA (MB)': 'sum'}).sort_values(by='USE_DATA (MB)',
                                                                                             ascending=False).iloc[
                    0]
            max_app_cnt = \
                user_app_usage.groupby('APP_NAME').agg({'USE_CNT': 'sum'}).sort_values(by='USE_CNT',
                                                                                       ascending=False).iloc[
                    0]
            max_app_time = \
                user_app_usage.groupby('APP_NAME').agg({'USE_TIME (MIN)': 'sum'}).sort_values(by='USE_TIME (MIN)',
                                                                                              ascending=False).iloc[0]

            tags = [f'高频使用{max_app_cnt.name}', f'{lac_max_area}']
            if user_avg_fee > 250:
                tags.append('高资费群体')
            else:
                tags.append('低资费群体')

            group_profile = GroupProfile(i,
                                         user_size,
                                         lac_size,
                                         lac_max_add,
                                         lac_max_lat,
                                         lac_max_lgtd,
                                         lac_max_city,
                                         lac_max_area,
                                         user_avg_fee,
                                         max_app_usage.name,
                                         max_app_usage.iloc[0],
                                         max_app_cnt.name,
                                         max_app_cnt.iloc[0],
                                         max_app_time.name,
                                         max_app_time.iloc[0],
                                         tags,
                                         subgraph
                                         )
            self.groups[i] = group_profile
        logging.info(f"自然人数据处理完成，共生成群体{len(self.groups)}个")


if __name__ == '__main__':
    group_profile = GroupProfile()
    print(group_profile.get_statics())
    nx.draw(group_profile.groups[1].subgraph)
    for i in group_profile.groups:
        print(group_profile.get_group(i))
    # plt.draw()
    # plt.show()
    # while True:
    #     print("sleep")
    #     time.sleep(1000)
